package SubjectBacktracking.Three;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

public class FindSubsequences {

/**
 * 难度：中等
 * 
 * 491. 递增子序列
 * 	给定一个整型数组, 你的任务是找到所有该数组的递增子序列，递增子序列的长度至少是2。
 * 	
 * 示例:
 * 	输入: [4, 6, 7, 7]
 * 	输出: [[4, 6], [4, 7], [4, 6, 7], [4, 6, 7, 7], [6, 7], [6, 7, 7], [7,7], [4,7,7]]
 * 	
 * 说明:
 * 	给定数组的长度不会超过15。
 * 	数组中的整数范围是 [-100,100]。
 * 	给定数组中可能包含重复数字，相等的数字应该被视为递增的一种情况。
 *
 * */
	
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		FindSubsequences fs = new FindSubsequences();
		System.out.println(fs.findSubsequences(new int[] {4,6,7,7}));
	}
	//自己写
	List<List<Integer>> result = new ArrayList<>();
	List<Integer> path = new ArrayList<>();
	public List<List<Integer>> findSubsequences(int[] nums) {
		backtracking(nums, 0);
		return result;
    }
	private void backtracking(int[] nums, int startIndex) {
		if(path.size()>1) {
			result.add(new ArrayList<>(path));
			// 注意这里不要加return，要取树上的节点
		}
		// 使用set对本层元素进行去重(同一父支点)
//		Set<Integer> uset = new HashSet<>();
		int[] used = new int[201]; //可以用数组代替
		for(int i=startIndex;i<nums.length;i++) {
			//选取的元素小于子序列最后一个元素，那么就不能是递增的 || 同层不能重复使用元素
//			if(!path.isEmpty()&&nums[i]<path.get(path.size()-1) || uset.contains(nums[i]))
			if(!path.isEmpty()&&nums[i]<path.get(path.size()-1) || used[nums[i]+100]==1)
				continue;
			// 记录这个元素在本层用过了，本层后面不能再用了
//			uset.add(nums[i]);
			used[nums[i]+100]=1;
			path.add(nums[i]);
			backtracking(nums, i+1);
			path.remove(path.size()-1);
		}
	}
	//方法一：二进制枚举 + 哈希
	List<Integer> temp = new ArrayList<Integer>();
    List<List<Integer>> ans = new ArrayList<List<Integer>>();
    Set<Integer> set = new HashSet<Integer>();
    int n;
	public List<List<Integer>> findSubsequences1(int[] nums) {
        n = nums.length;
        for (int i = 0; i < (1 << n); ++i) {
            findSubsequences1(i, nums);
            int hashValue = getHash(263, (int) 1E9 + 7);
            if (check() && !set.contains(hashValue)) {
                ans.add(new ArrayList<Integer>(temp));
                set.add(hashValue);
            }
        }
        return ans;
    }
    public void findSubsequences1(int mask, int[] nums) {
        temp.clear();
        for (int i = 0; i < n; ++i) {
            if ((mask & 1) != 0) {
                temp.add(nums[i]);
            }
            mask >>= 1;
        }
    }
    public int getHash(int base, int mod) {
        int hashValue = 0;
        for (int x : temp) {
            hashValue = hashValue * base % mod + (x + 101);
            hashValue %= mod;
        }
        return hashValue;
    }
    public boolean check() {
        for (int i = 1; i < temp.size(); ++i) {
            if (temp.get(i) < temp.get(i - 1)) {
                return false;
            }
        }
        return temp.size() >= 2;
    }
    //方法二：递归枚举 + 减枝
//    List<Integer> temp = new ArrayList<Integer>();
//    List<List<Integer>> ans = new ArrayList<List<Integer>>();
    public List<List<Integer>> findSubsequences2(int[] nums) {
        dfs(0, Integer.MIN_VALUE, nums);
        return ans;
    }
    public void dfs(int cur, int last, int[] nums) {
        if (cur == nums.length) {
            if (temp.size() >= 2) {
                ans.add(new ArrayList<Integer>(temp));
            }
            return;
        }
        if (nums[cur] >= last) {
            temp.add(nums[cur]);
            dfs(cur + 1, nums[cur], nums);
            temp.remove(temp.size() - 1);
        }
        if (nums[cur] != last) {
            dfs(cur + 1, last, nums);
        }
    }
}
